package com.cwz.gulimall.order.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.cwz.gulimall.common.to.SkuHasStockVo;
import com.cwz.gulimall.common.to.mq.OrderTo;
import com.cwz.gulimall.common.to.mq.SeckillOrderTo;
import com.cwz.gulimall.common.utils.Constant;
import com.cwz.gulimall.common.utils.R;
import com.cwz.gulimall.common.vo.MemberRespVo;
import com.cwz.gulimall.order.constant.OrderConstant;
import com.cwz.gulimall.order.dao.OrderItemDao;
import com.cwz.gulimall.order.entity.OrderItemEntity;
import com.cwz.gulimall.order.enume.OrderStatusEnum;
import com.cwz.gulimall.order.feign.CartFeignService;
import com.cwz.gulimall.order.feign.MemberFeignService;
import com.cwz.gulimall.order.feign.ProductFeignService;
import com.cwz.gulimall.order.feign.WareFeignService;
import com.cwz.gulimall.order.interceptor.LoginUserInterceptor;
import com.cwz.gulimall.order.service.OrderItemService;
import com.cwz.gulimall.order.to.OrderCreatTo;
import com.cwz.gulimall.order.vo.*;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cwz.gulimall.common.utils.PageUtils;
import com.cwz.gulimall.common.utils.Query;

import com.cwz.gulimall.order.dao.OrderDao;
import com.cwz.gulimall.order.entity.OrderEntity;
import com.cwz.gulimall.order.service.OrderService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;


@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    private ThreadLocal<OrderSubmitVo> orderSubmitVoThreadLocal = new ThreadLocal<>();

    @Autowired
    OrderItemService orderItemService;

    @Autowired
    MemberFeignService memberFeignService;

    @Autowired
    CartFeignService cartFeignService;

    @Autowired
    WareFeignService wareFeignService;

    @Autowired
    ProductFeignService productFeignService;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    ThreadPoolExecutor threadPoolExecutor;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
        MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        CompletableFuture<Void> addressTask = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<MemberAddressVo> address = memberFeignService.getAddress(memberRespVo.getId());
            orderConfirmVo.setAddress(address);
        }, threadPoolExecutor);

        CompletableFuture<Void> itemTask = CompletableFuture.runAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            orderConfirmVo.setItems(cartFeignService.getCurrentUserCartItem());
        }, threadPoolExecutor).thenRunAsync(() -> {
            List<OrderItemVo> items = orderConfirmVo.getItems();
            List<Long> skuIds = items.stream().map(OrderItemVo::getSkuId).collect(Collectors.toList());
            R hasStock = wareFeignService.getSkusHasStock(skuIds);
            if (hasStock.getCode() == 0) {
                List<SkuHasStockVo> data = hasStock.getData(new TypeReference<List<SkuHasStockVo>>() {
                });
                if (data != null) {
                    Map<Long, Boolean> collect = data.stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, SkuHasStockVo::getHasStock));
                    orderConfirmVo.setStocks(collect);
                }
            }
        }, threadPoolExecutor);
        orderConfirmVo.setIntegration(memberRespVo.getIntegration());
        String orderToken = UUID.randomUUID().toString().replace("-", "");
        stringRedisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberRespVo.getId(), orderToken, 30, TimeUnit.MINUTES);
        orderConfirmVo.setOrderToken(orderToken);
        CompletableFuture.allOf(addressTask, itemTask).get();
        return orderConfirmVo;
    }

    //    @GlobalTransactional
    @Transactional
    @Override
    public SubmitOrderResponseVo submitOrder(OrderSubmitVo orderSubmitVo) {
        orderSubmitVoThreadLocal.set(orderSubmitVo);
        SubmitOrderResponseVo submitOrderResponseVo = new SubmitOrderResponseVo();
        MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        String orderToken = orderSubmitVo.getOrderToken();
        Long result = stringRedisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberRespVo.getId()), orderToken);
        if (result == 1L) {
            //成功
            OrderCreatTo orderCreatTo = creatOrder();
            BigDecimal payPriceDataBase = orderCreatTo.getPayPrice();
            BigDecimal payPrice = orderSubmitVo.getPayPrice();
            if (Math.abs(payPriceDataBase.subtract(payPrice).doubleValue()) < 0.01) {
                saveOrder(orderCreatTo);
                WareSkuLockVo wareSkuLockVo = new WareSkuLockVo();
                wareSkuLockVo.setOrderSn(orderCreatTo.getOrderEntity().getOrderSn());
                List<OrderItemEntity> orderItems = orderCreatTo.getOrderItems();
                List<OrderItemVo> orderItemVos = orderItems.stream().map(item -> {
                    OrderItemVo orderItemVo = new OrderItemVo();
                    orderItemVo.setSkuId(item.getSkuId());
                    orderItemVo.setCount(item.getSkuQuantity());
                    orderItemVo.setTitle(item.getSkuName());
                    return orderItemVo;
                }).collect(Collectors.toList());
                wareSkuLockVo.setLocks(orderItemVos);
                R r = wareFeignService.orderLockStock(wareSkuLockVo);
                if (r.getCode() == 0) {
                    submitOrderResponseVo.setOrderEntity(creatOrder().getOrderEntity());
                    submitOrderResponseVo.setCode(0);
                    //订单创建成功，发送消息给RabbitMQ
                    rabbitTemplate.convertAndSend("order-event-exchange", "order.create.order", orderCreatTo.getOrderEntity());
                } else {
                    //锁库存失败
                    submitOrderResponseVo.setCode(3);
                }

            } else {
                //金额对比失败
                submitOrderResponseVo.setCode(2);
            }
        } else {
            //redis获取token失败
            submitOrderResponseVo.setCode(1);
        }
//        String redisToken = stringRedisTemplate.opsForValue().get(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberRespVo.getId());
//        if (orderToken != null && orderToken.equals(redisToken)) {
//            submitOrderResponseVo.setCode(0);
//
//        } else {
//            submitOrderResponseVo.setCode(1);
//        }
        return submitOrderResponseVo;
    }

    @Override
    public OrderEntity getOrderStatus(String orderSn) {
        return this.baseMapper.selectOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));
    }

    @Override
    public void closeOrder(OrderEntity orderEntity) {
        OrderEntity byId = this.getOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderEntity.getOrderSn()));
        if (byId != null && OrderStatusEnum.CREATE_NEW.getCode().equals(byId.getStatus())) {
            byId.setStatus(OrderStatusEnum.CANCLED.getCode());
            this.updateById(byId);
            OrderTo orderTo = new OrderTo();
            BeanUtils.copyProperties(byId, orderTo);
            rabbitTemplate.convertAndSend("order-event-exchange", "order.release.other", orderTo);
        }
    }

    @Override
    public void createSeckillOrder(SeckillOrderTo seckillOrderTo) {
        //TODO 根据seckillOrderTo里的数据查询数据库完善订单信息，存入数据库
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderSn(seckillOrderTo.getOrderSn());
        orderEntity.setCreateTime(new Date());
        orderEntity.setMemberId(seckillOrderTo.getMemberId());
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        BigDecimal multiply = seckillOrderTo.getSeckillPrice().multiply(new BigDecimal(seckillOrderTo.getNum()));
        orderEntity.setPayAmount(multiply);
        this.save(orderEntity);
        OrderItemEntity orderItemEntity = new OrderItemEntity();
        orderItemEntity.setOrderSn(seckillOrderTo.getOrderSn());
        orderItemEntity.setSkuId(seckillOrderTo.getSkuId());
        orderItemEntity.setSkuQuantity(seckillOrderTo.getNum());
        orderItemEntity.setRealAmount(multiply);
        orderItemService.save(orderItemEntity);
    }

    private void saveOrder(OrderCreatTo orderCreatTo) {
        OrderEntity orderEntity = orderCreatTo.getOrderEntity();
        orderEntity.setCreateTime(new Date());
        orderEntity.setModifyTime(new Date());
        this.save(orderEntity);
        List<OrderItemEntity> orderItems = orderCreatTo.getOrderItems();
        orderItemService.saveBatch(orderItems);
    }

    private OrderCreatTo creatOrder() {
        OrderCreatTo orderCreatTo = new OrderCreatTo();
        OrderEntity orderEntity = buildOrder();
        String orderSn = IdWorker.getTimeId();
        orderEntity.setOrderSn(orderSn);
        orderCreatTo.setOrderEntity(orderEntity);
        List<OrderItemEntity> orderItems = buildOrderItems(orderSn);
        orderCreatTo.setOrderItems(orderItems);
        orderCreatTo.setPayPrice(computePrice(orderEntity, orderItems));
        return orderCreatTo;
    }

    private BigDecimal computePrice(OrderEntity orderEntity, List<OrderItemEntity> orderItems) {
        BigDecimal total = new BigDecimal("0.0");
        BigDecimal promotion = new BigDecimal("0.0");
        BigDecimal coupon = new BigDecimal("0.0");
        BigDecimal integration = new BigDecimal("0.0");
        BigDecimal gift = new BigDecimal("0.0");
        BigDecimal growth = new BigDecimal("0.0");
        for (OrderItemEntity orderItem : orderItems) {
            total = total.add(orderItem.getRealAmount());
            promotion = promotion.add(orderItem.getPromotionAmount());
            coupon = coupon.add(orderItem.getCouponAmount());
            integration = integration.add(orderItem.getIntegrationAmount());
            gift = gift.add(new BigDecimal(orderItem.getGiftIntegration()));
            growth = growth.add(new BigDecimal(orderItem.getGiftGrowth()));
        }
        orderEntity.setTotalAmount(total);
        orderEntity.setPayAmount(total.add(orderEntity.getFreightAmount()));
        orderEntity.setPromotionAmount(promotion);
        orderEntity.setCouponAmount(coupon);
        orderEntity.setIntegrationAmount(integration);
        orderEntity.setIntegration(gift.intValue());
        orderEntity.setGrowth(growth.intValue());
        return orderEntity.getPayAmount();
    }

    private OrderEntity buildOrder() {
        MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setMemberId(memberRespVo.getId());
        OrderSubmitVo orderSubmitVo = orderSubmitVoThreadLocal.get();
        R fare = wareFeignService.getFare(orderSubmitVo.getAddrId());
        if (fare.getCode() == 0) {
            FareVo data = fare.getData(new TypeReference<FareVo>() {
            });
            orderEntity.setFreightAmount(data.getFare());
            orderEntity.setReceiverCity(data.getAddress().getCity());
            orderEntity.setReceiverDetailAddress(data.getAddress().getDetailAddress());
            orderEntity.setReceiverName(data.getAddress().getName());
            orderEntity.setReceiverPhone(data.getAddress().getPhone());
            orderEntity.setReceiverProvince(data.getAddress().getProvince());
            orderEntity.setReceiverPostCode(data.getAddress().getPostCode());
            orderEntity.setReceiverRegion(data.getAddress().getRegion());
        }
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        orderEntity.setAutoConfirmDay(7);
        orderEntity.setDeleteStatus(0);
        return orderEntity;
    }

    private List<OrderItemEntity> buildOrderItems(String orderSn) {
        List<OrderItemVo> currentUserCartItem = cartFeignService.getCurrentUserCartItem();
        if (currentUserCartItem != null && currentUserCartItem.size() > 0) {
            return currentUserCartItem.stream().map(orderItem -> {
                OrderItemEntity orderItemEntity = buildOrderItem(orderItem);
                orderItemEntity.setOrderSn(orderSn);
                return orderItemEntity;
            }).collect(Collectors.toList());
        }
        return null;
    }

    private OrderItemEntity buildOrderItem(OrderItemVo cartItem) {
        OrderItemEntity orderItemEntity = new OrderItemEntity();
        R r = productFeignService.getSpuInfoBySkuId(cartItem.getSkuId());
        SpuInfoVo data = r.getData(new TypeReference<SpuInfoVo>() {
        });
        orderItemEntity.setSpuId(data.getId());
        orderItemEntity.setSpuName(data.getSpuName());
        orderItemEntity.setSpuBrand(data.getBrandId().toString());
        orderItemEntity.setCategoryId(data.getCatelogId());
        orderItemEntity.setSkuId(cartItem.getSkuId());
        orderItemEntity.setSkuName(cartItem.getTitle());
        orderItemEntity.setSkuPic(cartItem.getImg());
        orderItemEntity.setSkuPrice(cartItem.getPrice());
        orderItemEntity.setSkuAttrsVals(StringUtils.collectionToDelimitedString(cartItem.getSkuAttr(), ";"));
        orderItemEntity.setSkuQuantity(cartItem.getCount());
        orderItemEntity.setGiftGrowth(cartItem.getPrice().multiply(new BigDecimal(orderItemEntity.getSkuQuantity())).intValue());
        orderItemEntity.setGiftIntegration(cartItem.getPrice().multiply(new BigDecimal(orderItemEntity.getSkuQuantity())).intValue());
        orderItemEntity.setPromotionAmount(new BigDecimal("0.0"));
        orderItemEntity.setCouponAmount(new BigDecimal("0.0"));
        orderItemEntity.setIntegrationAmount(new BigDecimal("0.0"));
        BigDecimal orign = orderItemEntity.getSkuPrice().multiply(new BigDecimal(orderItemEntity.getSkuQuantity().toString()));
        BigDecimal realAmount = orign.subtract(orderItemEntity.getPromotionAmount()).subtract(orderItemEntity.getCouponAmount()).subtract(orderItemEntity.getIntegrationAmount());
        orderItemEntity.setRealAmount(realAmount);
        return orderItemEntity;
    }

}